package com.gitee.fastmybatis.core.mapper;

import com.gitee.fastmybatis.core.FastmybatisContext;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.PageResult;
import com.gitee.fastmybatis.core.exception.QueryException;
import com.gitee.fastmybatis.core.ext.spi.BeanExecutor;
import com.gitee.fastmybatis.core.ext.spi.SpiContext;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.support.Getter;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.support.TreeNode;
import com.gitee.fastmybatis.core.util.ClassUtil;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.fastmybatis.core.util.TreeUtil;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.exceptions.TooManyResultsException;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 具备查询功能的Mapper
 *
 * @param <E> 实体类，如：Student
 * @author tanghc
 */
public interface SearchMapper<E> extends Mapper<E> {

    /**
     * 根据条件查询所有记录<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class)
     *         .eq(TUser::getState, 0)
     *         .in(TUser::getMoney, Arrays.asList(100, 1.0, 3));
     * List<TUser> list = mapper.list(query);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE state = ? AND money IN ( ? , ? , ? )
     * </pre>
     *
     * @param query 查询条件
     * @return 返回实体对象集合，没有返回空集合
     */
    List<E> list(@Param("query") Query query);

    /**
     * 查询返回Map，Map里面key对应数据库字段名/别名，value对应值
     * <pre>
     * {@literal
     * List<Map<String, Object>> listMap = mapper.listMap( query);
     * }
     * </pre>
     *
     * @param query 查询条件
     * @return 返回结果集，没有则返回空list
     */
    List<Map<String, Object>> listMap(@Param("query") Query query);

    /**
     * list查询并转换结果
     * <p>
     * <b>注:仅此方法支持group by查询</b>
     * </p>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class)
     *          .select("state, count(*) as cnt")
     *         .eq(TUser::getId, 3)
     *         .gt(TUser::getMoney, 1)
     *         .groupBy("state");
     * List<GroupVO> groupList = mapper.list(query, GroupVO.class);
     *
     * @Data
     * public class GroupVO {
     *     private Integer state;
     *     private Long cnt;
     * }
     * }
     * </pre>
     *
     * @param query 查询条件
     * @param clazz 待转换的class
     * @param <V>   类型
     * @return 返回结果
     */
    default <V> List<V> list(Query query, Class<V> clazz) {
        List<Map<String, Object>> list = listMap(query);
        if (list == null || list.isEmpty()) {
            return new ArrayList<>(0);
        }
        BeanExecutor beanExecutor = SpiContext.getBeanExecutor();
        return list.stream()
                .map(map -> beanExecutor.mapToPojo(map, clazz))
                .collect(Collectors.toList());
    }

    /**
     * 返回基本类型值
     * {@literal
     * Query query = Query.create()
     * .select("sum(money)")
     * .gt("id", 1);
     * BigDecimal value = userMapper.getPrimitive(query, BigDecimal.class).getOrThrow();
     * }
     *
     * @param query 查询条件
     * @param clazz 基本类型值,如:Integer.class, BigDecimal.class, Date.class,类型不对将抛出IllegalArgumentException异常
     * @param <V>   类型
     * @return 返回OneResult
     */
    default <V> OneResult<V> getPrimitive(Query query, Class<V> clazz) {
        if (!ClassUtil.isPrimitive(clazz.getSimpleName())) {
            throw new IllegalArgumentException("class '" + clazz.getName() + "' is not a primitive type.");
        }
        List<Map<String, Object>> list = listMap(query);
        if (list == null || list.isEmpty()) {
            return OneResult.empty();
        }
        int size = list.size();
        if (size > 1) {
            return OneResult.of(new TooManyResultsException("Expected one result (or null) to be returned by getOne(), but found: " + size));
        }
        Object val = new ArrayList<>(list.get(0).values()).get(0);
        if (val == null) {
            return OneResult.empty();
        }
        BeanExecutor beanExecutor = SpiContext.getBeanExecutor();
        V v = beanExecutor.parseValue(val, clazz);
        return OneResult.of(v);
    }

    /**
     * 根据条件查找单条记录(末尾加 limit 1)<br>
     * 如果要判断多条，参考 {@link #getOne(Query)}  }
     * <pre>
     * {@literal
     * // 查询id=3,金额大于1的用户
     * Query query = Query.query(TUser.class)
     *         .eq(TUser::getId, 3)
     *         .gt(TUser::getMoney, 1);
     * TUser user = mapper.get(query);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE id = ? AND money > ? LIMIT 1
     * </pre>
     *
     * @param query 查询条件
     * @return 返回实体对象，没有返回null
     * @see #getOne(Query)
     */
    default E get(Query query) {
        return getOne(query, true).get();
    }

    /**
     * 查询一条记录
     *
     * @param query 查询条件
     * @return 返回包装结果
     */
    default OneResult<E> getOne(Query query) {
        return getOne(query, false);
    }

    /**
     * 查询一条记录
     *
     * @param query          查询条件
     * @param appendLimitOne 是否追加limit 1,如果true，始终返回1条数据
     * @return 返回包装结果
     */
    default OneResult<E> getOne(Query query, boolean appendLimitOne) {
        if (appendLimitOne) {
            query.limit(0, 1);
        }
        List<E> list = list(query);
        return OneResult.of(list, appendLimitOne);
    }

    /**
     * 根据条件查找单条记录（追加limit 1）<br>
     * <pre>
     * {@literal
     * // 查询id=3,金额大于1的用户
     * Query query = Query.query(TUser.class)
     *         .eq(TUser::getId, 3)
     *         .gt(TUser::getMoney, 1);
     * Optional<TUser> userOpt = mapper.getOptional(query);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE id = ? AND money > ? LIMIT 1
     * </pre>
     *
     * @param query 查询条件
     * @return 返回Optional对象
     */
    default Optional<E> getOptional(Query query) {
        return Optional.ofNullable(get(query));
    }

    /**
     * 查询总记录数<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class).eq(TUser::getState, 0);
     * // 获取总数
     * long total = mapper.getCount(query);
     *
     * 对应SQL:
     * SELECT COUNT(*) FROM t_user WHERE `state` = 0
     * }
     * </pre>
     *
     * @param query 查询条件
     * @return 返回总记录数
     */
    default long getCount(Query query) {
        Query copy = query.copy();

        copy.setQueryAll(true)
                .setOrderInfo(null)
                .setSelectColumns(Collections.singletonList(copy.getCountExpression()));

        List<Map<String, Object>> mapList = listMap(copy);
        if (mapList.isEmpty()) {
            return 0;
        }
        Map<String, Object> row = mapList.get(0);
        Collection<Object> values = row.values();
        if (values.isEmpty()) {
            return 0;
        }
        Object cnt = values.iterator().next();
        return cnt instanceof Number ? ((Number) cnt).longValue() : Long.parseLong(String.valueOf(cnt));
    }

    /**
     * 查询单条数据返回指定字段并转换到指定类中(末尾加 limit 1)<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class).eq(TUser::getId, 6);
     * UserVO userVo = mapper.get(query, UserVO.class);
     * }
     * 对应SQL:
     * SELECT id , username FROM `t_user` t WHERE id = 6 AND LIMIT 0,1
     * </pre>
     *
     * @param query 查询条件
     * @param clazz 待转换的类，类中的字段类型必须跟实体类的中类型一致
     * @param <T>   转换类类型
     * @return 返回转换类，查不到返回null
     */
    default <T> T get(Query query, Class<T> clazz) {
        Objects.requireNonNull(clazz, "parameter 'clazz' can not null");
        E e = get(query);
        if (e == null) {
            return null;
        }
        return SpiContext.getBeanExecutor().copyBean(e, clazz);
    }

    /**
     * 查询单条数据返回指定字段并转换到指定类中(末尾加 limit 1)<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class).eq(TUser::getId, 6);
     * UserDTO userDTO = mapper.get(query, user -> {
     *     UserDTO userDTO = new UserDTO();
     *
     *     return userDTO;
     * });
     * }
     * 对应SQL:
     * SELECT id , username FROM `t_user` t WHERE id = 6 AND LIMIT 0,1
     * </pre>
     *
     * @param query     查询条件
     * @param converter 转换器
     * @param <T>       转换类类型
     * @return 返回转换类，查不到返回null
     */
    default <T> T get(Query query, Function<E, T> converter) {
        Objects.requireNonNull(converter, "parameter 'converter' can not null");
        return getOptional(query).map(converter).orElse(null);
    }

    /**
     * 查询某一行某个字段值(末尾加 limit 1)<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class).eq(TUser::getId, 6);
     * String username = mapper.getValue(query, TUser::getUsername);
     * }
     * 转换成SQL：
     * SELECT username FROM `t_user` t WHERE id = 6 LIMIT 0,1
     * </pre>
     *
     * @param query  查询条件
     * @param column 数据库字段
     * @return 返回单值，查不到返回null
     */
    default <R> R getValue(Query query, Getter<E, R> column) {
        String columnName = ClassUtil.getColumnName(column);
        query.setSelectColumns(Collections.singletonList(columnName));
        return getOptional(query)
                .map(column::get)
                .orElse(null);
    }

    /**
     * 查询某一行某个字段值(末尾加 limit 1)<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class).eq(TUser::getId, 6);
     * String username = mapper.getValueOptional(query, TUser::getUsername).orElse("");
     * }
     * 转换成SQL：
     * SELECT username FROM `t_user` t WHERE id = 6 LIMIT 0,1
     * </pre>
     *
     * @param query  查询条件
     * @param column 数据库字段
     * @return 返回Optional
     */
    default <R> Optional<R> getValueOptional(Query query, Getter<E, R> column) {
        return Optional.ofNullable(getValue(query, column));
    }

    /**
     * 根据主键查询<br>
     * <pre>
     * {@literal
     * TUser user = mapper.getById(3);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE id = 3
     * </pre>
     *
     * @param id 主键值
     * @return 返回实体对象，没有返回null
     */
    default E getById(Serializable id) {
        if (id == null) {
            return null;
        }
        String pkColumnName = FastmybatisContext.getPkColumnName(getEntityClass());
        Query query = Query.create().eq(pkColumnName, id).setQueryAll(true);
        List<E> list = list(query);
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 根据主键查询，返回Optional<br>
     * <pre>
     * {@literal
     * Optional<User> user = mapper.getById(3);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE id = 3
     * </pre>
     *
     * @param id 主键值
     * @return 返回实体对象，没有返回null
     */
    default Optional<E> getByIdOpt(Serializable id) {
        return Optional.ofNullable(getById(id));
    }

    /**
     * 根据主键查询强制查询，忽略逻辑删除字段<br>
     * <pre>
     * {@literal
     * TUser user = mapper.forceGetById(3);
     * }
     * 对应SQL:
     * SELECT col1, col2, ...
     * FROM `t_user` t
     * WHERE id = 3
     * </pre>
     *
     * @param id 主键值
     * @return 返回实体对象，没有返回null
     */
    default E forceGetById(Serializable id) {
        if (id == null) {
            return null;
        }
        String pkColumnName = FastmybatisContext.getPkColumnName(getEntityClass());
        Query query = Query.create().eq(pkColumnName, id).enableForceQuery();
        return get(query);
    }


    /**
     * 根据字段查询一条记录, (末尾加 limit 1)<br>
     * 如果要判断多条，参考 {@link #getOneByField }
     *
     * <pre>
     * {@literal
     * TUser user = mapper.get("username", "王五");
     * }
     * </pre>
     * <code>
     * SELECT col1,col2,... FROM table WHERE {column} = {value} LIMIT 1
     * </code>
     *
     * @param column 数据库字段名
     * @param value  字段值
     * @return 返回实体对象，没有返回null
     * @see #getOneByField(String, Object)
     */
    default E getByField(@Param("column") String column, @Param("value") Object value) {
        Query query = Query.create().eq(column, value);
        return getOne(query, true).get();
    }

    /**
     * 根据字段查询一条记录<br>
     * <pre>
     * {@literal
     * TUser user = mapper.get("username", "王五");
     * }
     * </pre>
     * <code>
     * SELECT col1,col2,... FROM table WHERE {column} = {value} LIMIT 1
     * </code>
     *
     * @param column 数据库字段名
     * @param value  字段值
     * @return 返回实OneResult
     */
    default OneResult<E> getOneByField(@Param("column") String column, @Param("value") Object value) {
        Query query = Query.create().eq(column, value);
        return getOne(query, false);
    }

    /**
     * 查询全部数据
     *
     * @return 返回全部数据，没有返回空list
     */
    default List<E> listAll() {
        return list(Query.create());
    }

    /**
     * 根据字段查询所有记录<br>
     *
     * <pre>
     * {@literal
     * List<TUser> list = mapper.list("age", 20);
     * }
     * </pre>
     * 对应SQL:
     * <code>
     * SELECT col1, col2, ... FROM t_user WHERE age = 20;
     * </code>
     *
     * @param column 字段
     * @param value  字段值,可以是单值也可以是集合，Number/String/Boolean/Collection/Array
     * @return 返回实体对象集合，没有返回空集合
     */
    default List<E> listByField(@Param("column") String column, @Param("value") Object value) {
        Query query = Query.create().addCondition(column, value);
        return list(query);
    }

    /**
     * 根据多个主键查询<br>
     * <pre>
     * {@literal
     * List<User> list = mapper.listByIds(Arrays.asList(1,2,3));
     * }
     * </pre>
     * <code>
     * SELECT col1, col2, ... FROM table WHERE id in (val1, val2, ...)
     * </code>
     *
     * @param ids id集合
     * @return 返回结果集，没有返回空list
     */
    default List<E> listByIds(Collection<? extends Serializable> ids) {
        if (ids == null || ids.isEmpty()) {
            return new ArrayList<>();
        }
        String pkColumnName = FastmybatisContext.getPkColumnName(getEntityClass());
        return listByField(pkColumnName, ids);
    }

    /**
     * 查询某一列的值
     * <pre>
     * {@literal
     * List<String> usernameList = mapper.listValue(query, TUser::getUsername);
     *
     * 对应SQL：SELECT username FROM t_user WHERE ...
     * }
     * </pre>
     *
     * @param query  查询条件
     * @param column 返回某一列
     * @param <R>    列类型
     * @return 返回某一列数据，没有返回空list
     */
    default <R> List<R> listValue(Query query, Getter<E, R> column) {
        String columnName = ClassUtil.getColumnName(column);
        query.setSelectColumns(Collections.singletonList(columnName));
        return list(query)
                .stream()
                .map(column::get)
                .collect(Collectors.toList());
    }

    /**
     * 查询某一列的值，并去重
     * <pre>
     * {@literal
     * List<String> usernameList = mapper.listUniqueValue(query, TUser::getUsername);
     *
     * 对应SQL：SELECT username FROM t_user WHERE ...
     * }
     * </pre>
     *
     * @param query  查询条件
     * @param column 返回某一列
     * @param <R>    列类型
     * @return 返回某一列数据，并去重，没有返回空Set
     */
    default <R> List<R> listUniqueValue(Query query, Getter<E, R> column) {
        return listValue(query, column).stream().distinct().collect(Collectors.toList());
    }

    /**
     * 分页查询，并转换结果
     * <pre>
     * {@literal
     * PageInfo<TUser> users = mapper.page(query, PageInfo::new);
     * }
     * </pre>
     *
     * @param query      查询条件
     * @param pageResult pageResult
     * @return 返回分页信息
     */
    default <P extends PageResult<E>> P page(Query query, Supplier<P> pageResult) {
        P result = pageResult.get();

        try {
            // 总页数
            int pageCount = 0;
            // 总条数
            long total = 0;
            // 每页记录数
            int pageSize = query.getLimit();
            // 结果集
            List<E> list = Collections.emptyList();

            // 如果是查询全部则直接返回结果集条数
            // 如果是分页查询则还需要带入条件执行一下sql
            if (query.getIsQueryAll()) {
                list = this.list(query);
                total = list.size();
                if (total > 0) {
                    pageCount = 1;
                }
            } else {
                if (query.getIsSetTotal()) {
                    //如果设置了total总记录数，直接获取该total
                    total = query.getTotal();
                } else {
                    //如果没有设置total，先去count执行一下sql
                    total = this.getCount(query);
                }
                // 如果有数据
                if (total > 0) {
                    list = this.list(query);

                    int start = query.getStart();
                    // 当前第几页
                    int pageIndex = (start / pageSize) + 1;

                    result.setStart(start);
                    result.setPageIndex(pageIndex);

                    pageCount = MapperUtil.calcPageCount(total, pageSize);
                }
            }
            result.setPageSize(pageSize);
            result.setList(list);
            result.setTotal(total);
            result.setPageCount(pageCount);
        } catch (Exception e) {
            throw new QueryException(e);
        }

        return result;
    }

    /**
     * 分页查询<br>
     * <pre>
     * {@literal
     * Query query = Query.query(TUser.class)
     *  .eq(TUser::getUsername, "张三")
     *  .page(1, 2) // 分页查询，按页码分，通常使用这种。
     * ;
     *
     * // 分页信息
     * PageInfo<TUser> pageInfo = mapper.page(query);
     *
     * List<TUser> list = pageInfo.getList(); // 结果集
     * long total = pageInfo.getTotal(); // 总记录数
     * int pageCount = pageInfo.getPageCount(); // 共几页
     * }
     * </pre>
     *
     * @param query 查询条件
     * @return 返回分页信息
     */
    default PageInfo<E> page(Query query) {
        return page(query, PageInfo::new);
    }

    /**
     * 查询结果集，并转换结果集中的记录，转换处理每一行<br>
     * <pre>
     * {@literal
     *  PageInfo<TUser> pageInfo = mapper.page(query, tUser -> {
     *      // 对每行数据进行转换
     *      String username = tUser.getUsername();
     *      if ("张三".equals(username)) {
     *          tUser.setUsername("法外狂徒");
     *      }
     *      return tUser;
     *   });
     * }
     * 或者：
     * {@literal
     *  // 对结果集进行手动转换，如果仅仅是属性拷贝可以直接：mapper.page(query, UserVO::new);
     *  PageInfo<UserVO> page = mapper.page(query, user -> {
     *      UserVO userVO = new UserVO();
     *      BeanUtils.copyProperties(user, userVO);
     *      return userVO;
     *   });
     * }
     * </pre>
     *
     * @param query     查询条件
     * @param converter 转换类
     * @return 返回分页信息
     */
    default <R> PageInfo<R> page(Query query, Function<E, R> converter) {
        PageInfo pageInfo = this.page(query);
        return pageInfo.convert(converter);
    }

    /**
     * 查询结果集，并转换结果集中的记录，转换处理list<br>
     * <pre>
     * {@literal
     * Query query = Query.create()
     *         .eq("state", 0);
     * PageInfo<UserVO> pageInfo = mapper.pageAndConvert(query, list -> {
     *     List<UserVO> retList = new ArrayList<>(list.size());
     *     for (TUser tUser : list) {
     *         UserVO userVO = new UserVO();
     *         BeanUtils.copyProperties(tUser, userVO);
     *         retList.add(userVO);
     *     }
     *     return retList;
     * });
     * }
     * </pre>
     *
     * @param query     查询条件
     * @param converter 转换类
     * @return 返回分页信息
     * @since 1.10.11
     */
    default <R> PageInfo<R> pageAndConvert(Query query, Function<List<E>, List<R>> converter) {
        PageInfo pageInfo = this.page(query);
        List<E> list = (List<E>) pageInfo.getList();
        List<R> retList = converter.apply(list);
        pageInfo.setList(retList);
        return (PageInfo<R>) pageInfo;
    }

    /**
     * 查询结果集，并转换结果集中的记录，并对记录进行额外处理<br>
     * <pre>
     * {@literal
     *  PageInfo<UserVO> page = mapper.page(query, UserVO::new, userVO -> {
     *      System.out.println(userVO.getUsername());
     *  });
     * }
     * </pre>
     *
     * @param query  查询条件
     * @param target 转换后的类
     * @param format 对转换后的类格式化，此时的对象已经完成属性拷贝
     * @param <R>    结果集类型
     * @return 返回PageInfo对象
     */
    default <R> PageInfo<R> page(Query query, Supplier<R> target, Consumer<R> format) {
        return this.page(query, t -> {
            R r = target.get();
            SpiContext.getBeanExecutor().copyProperties(t, r);
            format.accept(r);
            return r;
        });
    }

    /**
     * 查询返回easyui结果集<br>
     * 如果前端使用easyui，此返回结果可适用于easyui的datagrid组件
     *
     * @param query 查询条件
     * @return 返回easyui分页信息
     */
    default PageEasyui<E> pageEasyui(Query query) {
        return page(query, PageEasyui::new);
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象<br>
     * 如果key重复则抛出异常
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * Query query = Query.create()
     *         .ge("id", 1);
     * // id -> TUser
     * Map<Integer, TUser> map = mapper.getMap(query, TUser::getId);
     * }
     * </pre>
     *
     * @param query     查询条件
     * @param keyGetter 指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param <K>       key类型
     * @return 返回map对象
     */
    default <K> Map<K, E> getMap(Query query, Function<E, K> keyGetter) {
        return getMap(query, keyGetter, Function.identity());
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象<br>
     * 如果key重复则抛出异常
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * List<User> -> Map<Integer, User> // 键:id, 值:当前对象
     * List<User> -> Map<Integer, String> // 键:id, 值:name字段
     * }
     * </pre>
     *
     * @param query       查询条件
     * @param keyGetter   指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param valueGetter 指定map中的值
     * @param <K>         key类型
     * @param <V>         value类型
     * @return 返回map对象
     */
    default <K, V> Map<K, V> getMap(Query query, Function<E, K> keyGetter, Function<E, V> valueGetter) {
        return getMap(query, keyGetter, valueGetter, (u, v) -> {
            throw new IllegalStateException(String.format("Duplicate key %s", u));
        });
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * List<User> -> Map<Integer, User> // 键:id, 值:当前对象
     * List<User> -> Map<Integer, String> // 键:id, 值:name字段
     * }
     * </pre>
     *
     * @param query         查询条件
     * @param keyGetter     指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param valueGetter   指定map中的值
     * @param mergeFunction key冲突返回哪个值
     * @param <K>           key类型
     * @param <V>           value类型
     * @return 返回map对象
     */
    default <K, V> Map<K, V> getMap(Query query, Function<E, K> keyGetter, Function<E, V> valueGetter, BinaryOperator<V> mergeFunction) {
        return getMap(query, keyGetter, valueGetter, mergeFunction, LinkedHashMap::new);
    }

    /**
     * 查询结果并转换成Map对象<br>
     * 通过list中的某一列（如主键id）当做key返回map对象
     * <pre>
     * {@literal
     * public class User {
     *     private Integer id;
     *     private String name;
     * }
     *
     * List<User> -> Map<Integer, User> // 键:id, 值:当前对象
     * List<User> -> Map<Integer, String> // 键:id, 值:name字段
     * }
     * </pre>
     *
     * @param query         查询条件
     * @param keyGetter     指定map中的key，确保唯一性，一般使用主键id或唯一索引列
     * @param valueGetter   指定map中的值
     * @param mergeFunction key冲突返回哪个值
     * @param mapSupplier   构造map
     * @param <K>           key类型
     * @param <V>           value类型
     * @param <M>           Map类型
     * @return 返回map对象
     */
    default <K, V, M extends Map<K, V>> M getMap(Query query,
                                                 Function<E, K> keyGetter,
                                                 Function<E, V> valueGetter,
                                                 BinaryOperator<V> mergeFunction,
                                                 Supplier<M> mapSupplier) {
        return list(query).stream().collect(Collectors.toMap(keyGetter, valueGetter, mergeFunction, mapSupplier));
    }

    /**
     * 查询列表并将结果转换成树结构<br>
     * 实体类必须实现{@link TreeNode}接口
     * <pre>
     * {@literal
     * CREATE TABLE `menu` (
     *   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
     *   `name` varchar(64) NOT NULL COMMENT '菜单名称',
     *   `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '父节点',
     *   PRIMARY KEY (`id`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='菜单表';
     *
     * 实体类
     *
     * public class Menu implements TreeNode<Menu, Integer> {
     *
     *     private Integer id;
     *     private String name;
     *     private Integer parentId;
     *     private List<Menu> children;
     *
     *     @Override
     *     public Integer takeId() {
     *         return getId();
     *     }
     *
     *     @Override
     *     public Integer takeParentId() {
     *         return getParentId();
     *     }
     *
     *     @Override
     *     public void setChildren(List<Menu> children) {
     *         this.children = children;
     *     }
     *
     *     getter setter...
     * }
     *
     * List<Menu> treeData = mapper.listTreeData(query, 0);
     *
     * }
     * </pre>
     *
     * @param query  查询条件
     * @param rootId 根节点id值，一般为0
     * @param <T>    节点类型，必须实现{@link TreeNode}接口
     * @return 返回树列表
     */
    default <T extends TreeNode<T, Serializable>> List<T> listTree(Query query, Serializable rootId) {
        return listTree(query, rootId, e -> (T) e);
    }

    /**
     * 查询列表并将结果转换成树结构<br>
     * supplier返回的实体类必须实现{@link TreeNode}接口
     *
     * @param query    查询条件
     * @param rootId   根节点id值，一般为0
     * @param supplier 转换
     * @param <T>      节点类型，必须实现{@link TreeNode}接口
     * @return 返回树列表
     */
    default <T extends TreeNode<T, Serializable>> List<T> listTree(Query query, Serializable rootId, Supplier<T> supplier) {
        return listTree(query, rootId, e -> {
            T t = supplier.get();
            SpiContext.getBeanExecutor().copyProperties(e, t);
            return t;
        });
    }

    /**
     * 查询列表并将结果转换成树结构<br>
     * Function转换的返回类必须实现{@link TreeNode}接口
     *
     * @param query  查询条件
     * @param rootId 根节点id值，一般为0
     * @param <T>    节点类型，必须实现{@link TreeNode}接口
     * @return 返回树列表
     */
    default <T extends TreeNode<T, Serializable>> List<T> listTree(Query query, Serializable rootId, Function<E, T> converter) {
        List<T> list = list(query)
                .stream()
                .map(converter)
                .collect(Collectors.toList());
        return TreeUtil.convertTree(list, rootId);
    }

    /**
     * 根据主键id检查记录是否存在<br>
     * <code>boolean exist = mapper.checkExist(user)</code>
     *
     * @param entity 实体类
     * @return 返回true，记录存在
     */
    default boolean checkExist(E entity) {
        Object pkValue = FastmybatisContext.getPkValue(entity);
        if (pkValue == null) {
            return false;
        }
        String pkColumnName = FastmybatisContext.getPkColumnName(entity.getClass());
        return checkExist(pkColumnName, pkValue);
    }

    /**
     * 根据主键id检查记录是否存在<br>
     * <code>boolean exist = mapper.checkExistById(11)</code>
     *
     * @param id id值
     * @return 返回true，记录存在
     */
    default boolean checkExistById(Serializable id) {
        String pkColumnName = FastmybatisContext.getPkColumnName(getEntityClass());
        return checkExist(pkColumnName, id);
    }

    /**
     * 根据某个字段检查记录是否存在
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回true，记录存在
     */
    default boolean checkExist(String columnName, Object value) {
        return checkExist(columnName, value, null);
    }

    /**
     * 根据某个字段检查记录是否存在，且不是指定id的那条记录
     * <pre>
     *     SELECT brand_name FROM brand WHERE brand_name = ? and id != ?
     * </pre>
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @param id         需要排除的id值
     * @return 返回true，记录存在
     */
    default boolean checkExist(String columnName, Object value, Serializable id) {
        Objects.requireNonNull(columnName, "columnName can not null");
        Objects.requireNonNull(value, "value can not null");

        Query query = Query.create()
                .select(columnName)
                .eq(columnName, value);
        if (id != null) {
            String pkColumnName = FastmybatisContext.getPkColumnName(this.getEntityClass());
            query.notEq(pkColumnName, id);
        }
        E record = get(query);
        return record != null;
    }

}
